package com.czk.stream;

import org.junit.Test;

import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;

/**
 * jdk1.8新特性Collector
 *
 * @Author:ChenZhangKun
 * @Date: 2021/5/18 10:17
 */
public class CollectorDemo {
    private static List<Person> people = new ArrayList<>(10);

    static {
        //
        Person p1 = new Person("czk", 26);
        Person p2 = new Person("nzq", 25);
        Person p3 = new Person("czk", 27);
        people.add(p1);
        people.add(p2);
        people.add(p3);
        //
    }

    public static void main(String[] args) {

    }

    /**
     * 将集合中元素按照类型、条件过滤等归类，存放到指定类型的新集合，List、Map、Set、Collection或者ConcurrentMap。涉及以下方法：
     * <p>
     * Collectors.toList()
     * <p>
     * Collectors.toMap()
     * <p>
     * Collectors.toSet()
     * <p>
     * Collectors.toCollection()
     * <p>
     * Collectors.toConcurrentMap()
     */
    @Test
    public void fun_1() {
        // list
        List<String> collect = people.stream().map(Person::getName).collect(Collectors.toList());
        // 去重集合
        Set<String> collect2 = people.stream().map(Person::getName).collect(Collectors.toSet());
        // set
        Set<Integer> collect1 = people.stream().map(Person::getAge).collect(Collectors.toSet());
        System.out.println(collect);
        System.out.println(collect2);
        System.out.println(collect1);
    }

    /**
     * 按照条件对元素进行分组，和 SQL 中的 group by 用法有异曲同工之妙，通常也建议使用Java代码进行分组处理以减轻数据库SQL压力。
     * <p>
     * 分组涉及以下方法：
     * <p>
     * Collectors.groupingBy(…)：普通分组。
     * <p>
     * Collectors.groupingByConcurrent(…)：线程安全的分组。
     * <p>
     * 分组后，返回的是一个Map集合，其中key作为分组对象，value作为对应分组结果
     */
    @Test
    public void fun_2() {
        // 通过年龄分组
        Map<Integer, List<Person>> collect = people.stream().collect(Collectors.groupingBy(Person::getAge));
        System.out.println(collect);
        collect.forEach((key, value) -> {
            System.out.println(key);
            for (Person person : value) {
                System.out.println(person);
            }
        });
        // 按姓名分组
        Map<String, List<Person>> collect3 = people.stream().collect(Collectors.groupingBy(Person::getName));
        System.out.println(collect3);
        // 线程同步
        ConcurrentMap<String, List<Person>> collect1 = people.stream().collect(Collectors.groupingByConcurrent(Person::getName));
        // set去重复
        Map<Integer, Set<Person>> collect2 = people.stream().collect(Collectors.groupingBy(Person::getAge, Collectors.toSet()));
    }

    /**
     * 是分组的特殊情况，采用Collectors.partitioningBy(…)方法来完成。
     * <p>
     * 该方法实质是在做二分组，将符合条件、不符合条件的元素分组到两个key分别为true和false的Map中，从而我们能够得到符合和不符合的分组新集合。
     * <p>
     * 比如，People集合中人名有中文名，也有英文名，将人名按照中、英文名进行分区：
     */
    @Test
    public void fun_3() {
        // 按年龄大小进行分区
        Map<Boolean, List<Person>> collect = people.stream().collect(Collectors.partitioningBy(x -> x.getAge() > 25));
        // 拿到两个分区
        List<Person> people = collect.get(true);
        List<Person> people1 = collect.get(false);

        // 将名字不符合的分开
        Map<Boolean, List<Person>> listMap = people.stream().collect(Collectors.partitioningBy(x -> !Objects.equals(x.getName(), "czk")));
        System.out.println(listMap.get(true));
        System.out.println(listMap.get(false));
    }

    /**
     * 按照某个属性查找出最大或最小值元素，并且基于Comparator接口来对其进行比较，返回一个Optional对象，并结合Optional.isPresent()判断并取得最大或最小值。
     * <p>
     * 涉及以下方法：
     * <p>
     * Collectors.maxBy(…)：最大值。
     * Collectors.minBy(…)：最小值。
     */
    @Test
    public void fun_4() {
        // 拿到最大值
        Optional<Person> collect = people.stream().max(Comparator.comparing(Person::getAge));
        collect.ifPresent(System.out::println);
        // 拿到最小值
        Optional<Person> collect1 = people.stream().min(Comparator.comparing(Person::getAge));
        collect1.ifPresent(System.out::println);
    }

    /**
     * 用来完成累加计算、数据汇总(总数、总和、最小值、最大值、平均值)操作。
     * <p>
     * 计算集合某个属性的总和，类似与SQL中的sum函数。
     * <p>
     * 涉及以下方法：
     * <p>
     * Collectors.summingInt/Double/Long(…)：按照某个属性求和。
     * Collectors.summarizingInt/Double/Long(…)：按照某个属性的数据进行汇总，得到其总数、总和、最小值、最大值、平均值。
     */
    @Test
    public void fun_5() {
        // 计算全部年龄
        Integer collect = people.stream().collect(Collectors.summingInt(Person::getAge));
        System.out.println(collect);
        // 返回IntSummaryStatistics对象，里面包括平均值，最值等等
        IntSummaryStatistics collect1 = people.stream().collect(Collectors.summarizingInt(Person::getAge));
        System.out.println(collect1.getMax());
        System.out.println(collect1.getCount());
        System.out.println(collect1.getSum());
        System.out.println(collect1.getMin());
        System.out.println(collect1.getAverage());
    }

    /**
     * 将元素以某种规则连接起来，得到一个连接字符串。
     * <p>
     * 涉及以下方法：
     * <p>
     * Collectors.joining()：字符串直接连接。
     * Collectors.joining(CharSequence delimiter)：按照字符delimiter进行字符串连接。
     * Collectors.joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix)：
     * 按照前缀prefix，后缀suffix，并以字符delimiter进行字符串连接。
     */
    @Test
    public void fun_6() {
        // 直接连接
        String collect = people.stream().map(Person::getName).collect(Collectors.joining());
        System.out.println(collect);
        // 通过，进行连接
        String collect1 = people.stream().map(Person::getName).collect(Collectors.joining(","));
        System.out.println(collect1);
        // 通过前缀后缀进行连接
        String love = people.stream().map(Person::getName).collect(Collectors.joining("[", "love", "]"));
        System.out.println(love);
    }
}

class Person {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
